/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.compomics.fspike.contentprovider.fastaproviders.impl;

import com.compomics.fspike.fastaprocessing.FastaProcessing;
import com.compomics.fspike.fastaprocessing.FastaValidator;
import com.compomics.fspike.contentprovider.fastaproviders.FastaProvider;
import com.compomics.fspike.contentprovider.webprovider.UniprotContentProvider;
import com.compomics.util.gui.waiting.waitinghandlers.WaitingDialog;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ClassPathResource;

/**
 *
 * @author Kenneth
 */
public class DefaultFastaProvider implements FastaProvider {

    File spikedFasta;
    private FastaValidator validator = new FastaValidator();
    File cleanFastaFile;
    WaitingDialog dialog;

    /**
     *
     */
    public DefaultFastaProvider() {

    }

    /**
     *
     * @param dialog a waiting dialog that shows process in a gui
     */
    public void setWaitingDialog(WaitingDialog dialog) {
        this.dialog = dialog;
    }

    /**
     *
     * @param fastaFile the fasta file to write to
     * @throws Exception
     */
    public DefaultFastaProvider(File fastaFile) throws Exception {
        this.cleanFastaFile = fastaFile;
        spikedFasta = new File(fastaFile.getParentFile(), fastaFile.getName().replace(".fasta", "_spiked.fasta"));
        FileUtils.copyFile(cleanFastaFile, spikedFasta, true);
        validator.setFastaFile(spikedFasta);
    }

    @Override
    public void addSpikes(Map<String, String> contaminantMap) throws Exception {
        if (spikedFasta == null) {
            getCleanFastaFile();
        }
        addPossibleSpikes(spikedFasta, contaminantMap, validator);
    }

    @Override
    public void addSpikes(List<String> uniprotAcessions) throws Exception {
        if (spikedFasta == null) {
            getCleanFastaFile();
        }
        Map<String, String> uniprotContaminantSequences = UniprotContentProvider.getUniprotContaminantSequences(uniprotAcessions);
        addSpikes(uniprotContaminantSequences);
    }

    @Override
    public void addSingleSpike(String contaminantName, String contaminantSequence) throws Exception {
        if (!validator.hasSpike(contaminantSequence)) {
            if (spikedFasta == null) {
                getCleanFastaFile();
            }
            addPossibleSpike(spikedFasta, contaminantName, contaminantSequence, validator);
        }
    }

    @Override
    public void addSingleSpike(String uniprotAcession) throws Exception {
        if (spikedFasta == null) {
            getCleanFastaFile();
        }
        String uniprotContaminantSequence = UniprotContentProvider.getUniprotSpikeSequenceByAccession(uniprotAcession);
        addSingleSpike(uniprotAcession, uniprotContaminantSequence);
    }

    @Override
    public void addDefaultSpikes() throws Exception {
        if (spikedFasta == null) {
            getCleanFastaFile();
        }
        addDefaultSpikes(spikedFasta);
        FastaProcessing.removeStopCodonsAndLineBreaks(spikedFasta, 1, Integer.MAX_VALUE);
    }

    @Override
    public File getCleanFastaFile() throws Exception {
        return cleanFastaFile;
    }

    @Override
    public File getContaminatedFastaFile() {
        return spikedFasta;
    }

    /**
     * sets the fasta to add spikes to
     *
     * @param contaminatedFastaFile
     * @throws Exception
     */
    public void setContaminatedFastaFile(File contaminatedFastaFile) throws Exception {
        validator.setFastaFile(contaminatedFastaFile);
        this.spikedFasta = contaminatedFastaFile;
    }

    /**
     *
     * @return the validator for this provider. A Validator checks for duplicate
     * entries in the fasta
     */
    public FastaValidator getValidator() {
        return validator;
    }

    public void setValidator(FastaValidator validator) {
        this.validator = validator;
    }

    public void setCleanFastaFile(File cleanFastaFile) {
        this.cleanFastaFile = cleanFastaFile;
    }

    public void saveContaminatedFasta(File outputFile) throws IOException {
        if (!spikedFasta.equals(outputFile)) {
            FileUtils.copyFile(spikedFasta, outputFile);
        }
        spikedFasta.delete();
    }

    public void saveCleanFasta(File outputFile) throws IOException {
        if (!cleanFastaFile.equals(outputFile)) {
            FileUtils.copyFile(cleanFastaFile, outputFile);
        }
        cleanFastaFile.delete();
    }

    /**
     * Adds the default contaminants to the fasta file, as specified in
     * contaminants.fasta
     *
     * @param fastaFile
     * @throws Exception
     */
    public void addDefaultSpikes(File fastaFile) throws Exception {
        if (dialog != null) {
            dialog.setSecondaryProgressCounterIndeterminate(true);
        }
        validator = new FastaValidator(dialog);
        validator.setFastaFile(fastaFile);
        dialog.appendReport("Loading default spikes...", true, false);
        try (InputStream inputStream = new ClassPathResource("contaminants.fasta").getInputStream(); BufferedWriter writer = new BufferedWriter(new FileWriter(fastaFile, true))) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            String contaminantHeader = "";
            String contaminantSequence = "";
            while ((line = reader.readLine()) != null) {
                if (!line.startsWith(">")) {
                    contaminantSequence += line;
                } else {
                    if (!validator.hasSpike(contaminantSequence)) {
                        writer.write(new StringBuilder(contaminantHeader)
                                .append("contaminant")
                                .append(System.lineSeparator())
                                .append(contaminantSequence)
                                .append(System.lineSeparator()).toString());
                        writer.flush();
                    }
                    contaminantHeader = line;
                }
            }
            writer.flush();
        }
        dialog.appendReport("\t\tdone", false, true);
        if (dialog != null) {
            dialog.setSecondaryProgressCounterIndeterminate(false);
            dialog.resetSecondaryProgressCounter();
        }
    }

    /**
     * Adds a single spike in the fastaFile while making sure it's not a
     * duplicate entry
     *
     * @param fastaFile the fastafile to concatenate to
     * @param spikeName
     * @param spikeSequence
     * @param validator fasta validator to ensure no duplicates are added
     * @throws IOException
     */
    public void addPossibleSpike(File fastaFile, String spikeName, String spikeSequence, FastaValidator validator) throws IOException {
        if (!validator.hasSpike(spikeSequence)) {
            try (FileWriter writer = new FileWriter(fastaFile, true)) {
                if (dialog != null) {
                    dialog.appendReport("Adding spike :" + spikeName, true, false);
                }
                String fastaEntry = makeFastaEntry(spikeName, spikeSequence);
                writer.append(fastaEntry);
                writer.flush();
                if (dialog != null) {
                    dialog.appendReport("\t\tdone", false, true);
                }
            }
        } else if (dialog != null) {
            dialog.appendReport("Duplicate spike :" + spikeName, true, true);
        }
    }

    /**
     * Adds a map of spikes in the fastaFile while making sure it's not a
     * duplicate entry
     *
     * @param fastaFile the fastafile to concatenate to
     * @param spikeMap the map bearing spikes and their sequences
     * @param validator fasta validator to ensure no duplicates are added
     * @throws IOException
     */
    public void addPossibleSpikes(File fastaFile, Map<String, String> spikeMap, FastaValidator validator) throws IOException {
        for (String aContaminant : spikeMap.keySet()) {
            addPossibleSpike(fastaFile, aContaminant, spikeMap.get(aContaminant), validator);
        }
    }

    private String makeFastaEntry(String contaminantName, String contaminantSequence) {
        return new StringBuilder(">ct|").append(contaminantName).append("|contaminant").append(System.lineSeparator()).append(contaminantSequence).append(System.lineSeparator()).toString();
    }

}
